import { resolve } from "path";
import { DefinePlugin, IgnorePlugin } from "webpack";
import { CleanWebpackPlugin } from "clean-webpack-plugin";
import CopyWebpackPlugin from "copy-webpack-plugin";
import CssMinimizerPlugin from "css-minimizer-webpack-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import TerserPlugin from "terser-webpack-plugin";
import WebpackBar from 'webpackbar';
import ClientPlugin from "./clientPlugin";

import { getAliases, getEntry, getHtmlTemplate } from "./paths";
import { CSSOptions, EnvType, WebpackConfigType } from "./types";

const config = (
  commandName: string,
  env: EnvType = {
    NODE_ENV: process.env.NODE_ENV || "",
  },
  css: CSSOptions = {}
): WebpackConfigType => {
  const isServe = commandName === "serve";
  const isHot =
    env.NODE_ENV === "development" ||
    env.WEBPACK_WATCH ||
    env.WEBPACK_SERVE ||
    isServe;
  const isBuild =
    env.NODE_ENV === "production" || env.WEBPACK_BUILD || !isServe;
  const isDevSourcemap = !!(css && css.devSourcemap);
  const cssLoader = [
    isHot && require.resolve("style-loader"),
    isBuild && {
      loader: MiniCssExtractPlugin.loader,
      options: {
        esModule: false,
      },
    },
    {
      loader: require.resolve("css-loader"),
      options: {
        modules: {
          auto: /\.module\.\w+$/i,
          localIdentName: "_[local]_[hash:base64:8]",
          exportLocalsConvention: "camelCase",
        },
        sourceMap: isHot || isDevSourcemap,
      },
    },
    {
      loader: require.resolve("postcss-loader"),
      options: {
        sourceMap: isHot || isDevSourcemap,
        postcssOptions: {
          plugins: [
            require.resolve("postcss-flexbugs-fixes"),
            [
              require.resolve("postcss-preset-env"),
              {
                autoprefixer: {
                  flexbox: "no-2009",
                },
                stage: 3,
              },
            ],
            require.resolve("postcss-normalize"),
          ],
        },
      },
    },
  ];

  return {
    devServer: {
      hot: true,
      compress: true,
      devMiddleware: {
        publicPath: "/",
      },
      historyApiFallback: {
        disableDotRule: true,
      },
      allowedHosts: "all",
      headers: {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "*",
        "Access-Control-Allow-Headers": "*",
      },
      static: {
        directory: resolve("public"),
      },
      client: {
        progress: false
      }
    },
    infrastructureLogging: {
      level: 'warn'
    },
    entry: getEntry(),
    output: {
      path: resolve("dist"),
      publicPath: "/",
      filename: `assets/[name].[chunkhash:8].js`,
      chunkFilename: `assets/[name].[chunkhash:8].js`,
      assetModuleFilename: "assets/[name].[contenthash:8].[ext]",
      pathinfo: false,
    },
    mode: isHot ? "development" : "production",
    stats: {
      colors: true,
      chunks: false,
      chunkModules: false,
      chunkOrigins: false,
      modules: false,
      moduleTrace: false,
      source: false,
      warnings: true,

      // 打包后的列表
      assets: isBuild,
      entrypoints: isBuild,
      children: false,
    },
    resolve: {
      modules: ["node_modules"],
      extensions: [
        ".js",
        ".jsx",
        ".ts",
        ".tsx",
        ".json",
        ".less",
        ".css",
        ".mjs",
        ".svelte",
        ".vue",
        ".gql",
        ".graphql",
      ],
      alias: getAliases(),
    },
    module: {
      rules: [
        {
          test: /\.js$/,
          use: [
            {
              loader: "esbuild-loader",
              options: {
                loader: "jsx",
                target: "es2015",
              },
            },
          ],
          exclude: /node_modules/,
        },
        {
          test: /\.ts$/,
          use: [
            {
              loader: "esbuild-loader",
              options: {
                loader: "ts",
              },
            },
          ],
          exclude: /node_modules/,
        },
        {
          test: /\.tsx?$/,
          use: [
            {
              loader: "esbuild-loader",
              options: {
                loader: "tsx",
                target: "es2015",
              },
            },
          ],
          exclude: /node_modules/,
        },
        {
          test: /\.(woff|woff2|eot|ttf|otf)$/,
          type: "asset/resource",
        },
        {
          test: /\.(png|svg|jpg|jpeg|gif)$/,
          type: "asset/resource",
        },
        {
          test: /\.css$/,
          use: cssLoader.filter(Boolean),
        },
        {
          test: /\.less$/,
          use: [
            ...cssLoader,
            {
              loader: "less-loader",
              options: {
                sourceMap: isHot || isDevSourcemap,
                lessOptions: {
                  javascriptEnabled: true,
                },
                implementation: require.resolve("less"),
                ...((css &&
                  css.preprocessorOptions &&
                  css.preprocessorOptions.less) ||
                  {}),
              },
            },
          ].filter(Boolean),
        },
        {
          test: /\.s[ac]ss$/i,
          use: [
            ...cssLoader,
            {
              loader: "sass-loader",
              options: {
                implementation: require.resolve("sass"),
                sassOptions: {
                  includePaths: ["node_modules"],
                },
                sourceMap: isHot || isDevSourcemap,
                ...((css &&
                  css.preprocessorOptions &&
                  css.preprocessorOptions.scss) ||
                  {}),
              },
            },
          ].filter(Boolean),
        },
      ].filter(Boolean),
    },
    plugins: [
      new DefinePlugin({
        "process.env": JSON.stringify(process.env),
      }),
      new IgnorePlugin({
        resourceRegExp: /^\.\/locale$/,
        contextRegExp: /moment$/,
      }),
      new HtmlWebpackPlugin({
        meta: false,
        inject: true,
        template: getHtmlTemplate(),
        minify: isHot
          ? false
          : {
              removeComments: true,
              collapseWhitespace: true,
              removeRedundantAttributes: true,
              useShortDoctype: true,
              removeEmptyAttributes: true,
              removeStyleLinkTypeAttributes: true,
              keepClosingSlash: true,
              minifyJS: true,
              minifyCSS: true,
              minifyURLs: true,
            },
      }),
      !isBuild && new WebpackBar({
        name: 'Client',
      }),
      !isBuild && new ClientPlugin(),
      isBuild && new CleanWebpackPlugin(),
      isBuild &&
        new CopyWebpackPlugin({
          patterns: [
            {
              from: "./public",
              globOptions: {
                ignore: ["**/index.html"],
              },
            },
          ],
        }),
      isBuild &&
        new MiniCssExtractPlugin({
          filename: `assets/[name].[contenthash:8].css`,
          chunkFilename: `assets/[name].[contenthash:8].css`,
          ignoreOrder: true,
        }),
    ].filter(Boolean),
    optimization: {
      nodeEnv: process.env.NODE_ENV,
      runtimeChunk: true,
      minimize: isBuild,
      minimizer: isBuild
        ? [
            new TerserPlugin({
              terserOptions: {
                compress: {
                  ecma: 5,
                  comparisons: false,
                  inline: 2,
                },
                mangle: {
                  safari10: true,
                },
                output: {
                  ecma: 5,
                  comments: false,
                  ascii_only: true,
                },
              },
              extractComments: false,
            }),
            new CssMinimizerPlugin(),
          ]
        : [],
      splitChunks: isBuild
        ? {
            chunks: "all",
            cacheGroups: {
              defaultVendors: {
                test: /[\\/]node_modules[\\/]/,
                priority: -10,
                chunks: "all",
              },
              default: {
                minChunks: 2,
                chunks: "all",
                priority: -20,
                reuseExistingChunk: true,
              },
            },
          }
        : false,
    },
    performance: {
      hints: false,
    },
    target: "web",
  };
};

export default config;
